package sardine;

import io.netty.handler.codec.http.HttpResponseStatus;
import sardine.exception.ExceptionHandler;
import sardine.exception.ExceptionMapper;
import sardine.exception.SimpleExceptionHandler;
import sardine.function.Condition;
import sardine.function.Filter;
import sardine.function.Filter.VFilter;
import sardine.function.Route;
import sardine.function.Route.VRoute;
import sardine.mvc.ModelAndView;
import sardine.mvc.TemplateEngine;
import sardine.mvc.TemplateViewRoute;

import java.util.Map;
import java.util.Optional;

/**
 * @auth bruce-sha
 * @date 2015/5/21
 */
public final class Sardine extends SardineBase {

    private Sardine() {
    }

    /* ***************************** 空参路由 **************************** */

    public static void get(final String path, final VRoute route) {
        get(path, (request, response) -> route.apply());
    }

    public static void post(final String path, final VRoute route) {
        post(path, (request, response) -> route.apply());
    }

    public static void put(final String path, final VRoute route) {
        put(path, (request, response) -> route.apply());
    }

    public static void patch(final String path, final VRoute route) {
        patch(path, (request, response) -> route.apply());
    }

    public static void delete(final String path, final VRoute route) {
        delete(path, (request, response) -> route.apply());
    }

    public static void head(final String path, final VRoute route) {
        head(path, (request, response) -> route.apply());
    }

    public static void trace(final String path, final VRoute route) {
        trace(path, (request, response) -> route.apply());
    }

    public static void connect(final String path, final VRoute route) {
        connect(path, (request, response) -> route.apply());
    }

    public static void options(final String path, final VRoute route) {
        options(path, (request, response) -> route.apply());
    }

    public static void before(final String path, final VRoute route) {
        before(path, (request, response) -> route.apply());
    }

    public static void after(final String path, final VRoute route) {
        after(path, (request, response) -> route.apply());
    }

    /* ***************************** 全参路由 **************************** */

    public static void get(final String path, final Route route) {
        route(HttpMethod.GET, build(path, route));
    }

    public static void post(final String path, final Route route) {
        route(HttpMethod.POST, build(path, route));
    }

    public static void put(final String path, final Route route) {
        route(HttpMethod.PUT, build(path, route));
    }

    public static void patch(final String path, final Route route) {
        route(HttpMethod.PATCH, build(path, route));
    }

    public static void delete(final String path, final Route route) {
        route(HttpMethod.DELETE, build(path, route));
    }

    public static void head(final String path, final Route route) {
        route(HttpMethod.HEAD, build(path, route));
    }

    public static void trace(final String path, final Route route) {
        route(HttpMethod.TRACE, build(path, route));
    }

    public static void connect(final String path, final Route route) {
        route(HttpMethod.CONNECT, build(path, route));
    }

    public static void options(final String path, final Route route) {
        route(HttpMethod.OPTIONS, build(path, route));
    }

     /* ***************************** 条件路由 **************************** */

    public static void get(final String path, final Condition condition, final Route route) {
        get(path, route);
    }

    public static void post(final String path, final Condition condition, final Route route) {
        post(path, route);
    }

      /* ***************************** 全参路由带 accept type **************************** */

    public static void get(final String path, final String acceptType, final Route route) {
        route(HttpMethod.GET, build(path, Optional.ofNullable(acceptType), route));
    }

    public static void post(final String path, final String acceptType, final Route route) {
        route(HttpMethod.POST, build(path, Optional.ofNullable(acceptType), route));
    }

    public static void put(final String path, final String acceptType, final Route route) {
        route(HttpMethod.PUT, build(path, Optional.ofNullable(acceptType), route));
    }

    public static void patch(final String path, final String acceptType, final Route route) {
        route(HttpMethod.PATCH, build(path, Optional.ofNullable(acceptType), route));
    }

    public static void delete(final String path, final String acceptType, final Route route) {
        route(HttpMethod.DELETE, build(path, Optional.ofNullable(acceptType), route));
    }

    public static void head(final String path, final String acceptType, final Route route) {
        route(HttpMethod.HEAD, build(path, Optional.ofNullable(acceptType), route));
    }

    public static void trace(final String path, final String acceptType, final Route route) {
        route(HttpMethod.TRACE, build(path, Optional.ofNullable(acceptType), route));
    }

    public static void connect(final String path, final String acceptType, final Route route) {
        route(HttpMethod.CONNECT, build(path, Optional.ofNullable(acceptType), route));
    }

    public static void options(final String path, final String acceptType, final Route route) {
        route(HttpMethod.OPTIONS, build(path, Optional.ofNullable(acceptType), route));
    }


    /* ***************************** 过滤器 **************************** */

    public static void before(final VFilter filter) {
        before((request, response) -> filter.apply());
    }

    public static void after(final VFilter filter) {
        after((request, response) -> filter.apply());
    }

    public static void before(final Filter filter) {
        before(SardineBase.ALL_PATHS, filter);
    }

    public static void after(final Filter filter) {
        after(SardineBase.ALL_PATHS, filter);
    }

    public static void before(final String path, final Filter filter) {
        before(path, DEFAULT_ACCEPT_TYPE, filter);
    }

    public static void after(final String path, final Filter filter) {
        after(path, DEFAULT_ACCEPT_TYPE, filter);
    }

    public static void before(final String path, final String acceptType, final Filter filter) {
        filter(HttpMethod.BEFORE, build(path, Optional.ofNullable(acceptType), filter));
    }

    public static void after(final String path, final String acceptType, final Filter filter) {
        filter(HttpMethod.AFTER, build(path, Optional.ofNullable(acceptType), filter));
    }


    /* ***************************** 模板引擎 **************************** */

    public static void get(final String path,
                           final TemplateViewRoute route, final TemplateEngine engine) {
        route(HttpMethod.GET, SimpleTemplateViewRoute.create(path, route, engine));
    }

    public static void get(final String path, final String acceptType,
                           final TemplateViewRoute route, final TemplateEngine engine) {
        route(HttpMethod.GET, SimpleTemplateViewRoute.create(path, acceptType, route, engine));
    }

    public static void post(final String path,
                            final TemplateViewRoute route, final TemplateEngine engine) {
        route(HttpMethod.POST, SimpleTemplateViewRoute.create(path, route, engine));
    }

    public static void post(final String path, final String acceptType,
                            final TemplateViewRoute route, final TemplateEngine engine) {
        route(HttpMethod.POST, SimpleTemplateViewRoute.create(path, acceptType, route, engine));
    }

    public static void put(final String path,
                           final TemplateViewRoute route, final TemplateEngine engine) {
        route(HttpMethod.PUT, SimpleTemplateViewRoute.create(path, route, engine));
    }

    public static void put(final String path, final String acceptType,
                           final TemplateViewRoute route, final TemplateEngine engine) {
        route(HttpMethod.PUT, SimpleTemplateViewRoute.create(path, acceptType, route, engine));
    }

    public static void delete(final String path,
                              final TemplateViewRoute route, final TemplateEngine engine) {
        route(HttpMethod.DELETE, SimpleTemplateViewRoute.create(path, route, engine));
    }

    public static void delete(final String path, final String acceptType,
                              final TemplateViewRoute route, final TemplateEngine engine) {
        route(HttpMethod.DELETE, SimpleTemplateViewRoute.create(path, acceptType, route, engine));
    }

    public static void patch(final String path,
                             final TemplateViewRoute route, final TemplateEngine engine) {
        route(HttpMethod.PATCH, SimpleTemplateViewRoute.create(path, route, engine));
    }

    public static void patch(final String path, final String acceptType,
                             final TemplateViewRoute route, final TemplateEngine engine) {
        route(HttpMethod.PATCH, SimpleTemplateViewRoute.create(path, acceptType, route, engine));
    }

    public static void head(final String path,
                            final TemplateViewRoute route, final TemplateEngine engine) {
        route(HttpMethod.HEAD, SimpleTemplateViewRoute.create(path, route, engine));
    }

    public static void head(final String path, final String acceptType,
                            final TemplateViewRoute route, final TemplateEngine engine) {
        route(HttpMethod.HEAD, SimpleTemplateViewRoute.create(path, acceptType, route, engine));
    }

    public static void trace(final String path,
                             final TemplateViewRoute route, final TemplateEngine engine) {
        route(HttpMethod.TRACE, SimpleTemplateViewRoute.create(path, route, engine));
    }

    public static void trace(final String path, final String acceptType,
                             final TemplateViewRoute route, final TemplateEngine engine) {
        route(HttpMethod.TRACE, SimpleTemplateViewRoute.create(path, acceptType, route, engine));
    }

    public static void connect(final String path,
                               final TemplateViewRoute route, final TemplateEngine engine) {
        route(HttpMethod.CONNECT, SimpleTemplateViewRoute.create(path, route, engine));
    }

    public static void connect(final String path, final String acceptType,
                               final TemplateViewRoute route, final TemplateEngine engine) {
        route(HttpMethod.CONNECT, SimpleTemplateViewRoute.create(path, acceptType, route, engine));
    }

    public static void options(final String path,
                               final TemplateViewRoute route, final TemplateEngine engine) {
        route(HttpMethod.OPTIONS, SimpleTemplateViewRoute.create(path, route, engine));
    }

    public static void options(final String path, final String acceptType,
                               final TemplateViewRoute route, final TemplateEngine engine) {
        route(HttpMethod.OPTIONS, SimpleTemplateViewRoute.create(path, acceptType, route, engine));
    }

    //////////////////////////////////////////////////
    // BEGIN Response Transforming Routes
    //////////////////////////////////////////////////

    /**
     * Map the route for HTTP GET requests
     *
     * @param path        the path
     * @param route       The route
     * @param transformer the response transformer
     */
    public static <T> void get(final String path, final Route<T> route, ResponseTransformer<T> transformer) {
        route(HttpMethod.GET, SimpleResponseTransformerRoute.create(path, route, transformer));
    }

    /**
     * Map the route for HTTP GET requests
     *
     * @param path        the path
     * @param acceptType  the accept type
     * @param route       The route
     * @param transformer the response transformer
     */
    public static void get(String path, String acceptType, Route route, ResponseTransformer transformer) {
        route(HttpMethod.GET, SimpleResponseTransformerRoute.create(path, acceptType, route, transformer));
    }

    /**
     * Map the route for HTTP POST requests
     *
     * @param path        the path
     * @param route       The route
     * @param transformer the response transformer
     */
    public static void post(final String path, final Route route, ResponseTransformer transformer) {
        route(HttpMethod.POST, SimpleResponseTransformerRoute.create(path, route, transformer));
    }

    /**
     * Map the route for HTTP POST requests
     *
     * @param path        the path
     * @param acceptType  the accept type
     * @param route       The route
     * @param transformer the response transformer
     */
    public static void post(String path, String acceptType, Route route, ResponseTransformer transformer) {
        route(HttpMethod.POST, SimpleResponseTransformerRoute.create(path, acceptType, route, transformer));
    }

    /**
     * Map the route for HTTP PUT requests
     *
     * @param path        the path
     * @param route       The route
     * @param transformer the response transformer
     */
    public static void put(final String path, final Route route, ResponseTransformer transformer) {
        route(HttpMethod.PUT, SimpleResponseTransformerRoute.create(path, route, transformer));
    }

    /**
     * Map the route for HTTP PUT requests
     *
     * @param path        the path
     * @param acceptType  the accept type
     * @param route       The route
     * @param transformer the response transformer
     */
    public static void put(String path, String acceptType, Route route, ResponseTransformer transformer) {
        route(HttpMethod.PUT, SimpleResponseTransformerRoute.create(path, acceptType, route, transformer));
    }

    /**
     * Map the route for HTTP DELETE requests
     *
     * @param path        the path
     * @param route       The route
     * @param transformer the response transformer
     */
    public static void delete(final String path, final Route route, ResponseTransformer transformer) {
        route(HttpMethod.DELETE, SimpleResponseTransformerRoute.create(path, route, transformer));
    }

    /**
     * Map the route for HTTP DELETE requests
     *
     * @param path        the path
     * @param acceptType  the accept type
     * @param route       The route
     * @param transformer the response transformer
     */
    public static void delete(String path,
                              String acceptType,
                              Route route,
                              ResponseTransformer transformer) {
        route(HttpMethod.DELETE, SimpleResponseTransformerRoute.create(path, acceptType, route, transformer));
    }

    /**
     * Map the route for HTTP HEAD requests
     *
     * @param path        the path
     * @param route       The route
     * @param transformer the response transformer
     */
    public static void head(final String path, final Route route, ResponseTransformer transformer) {
        route(HttpMethod.HEAD, SimpleResponseTransformerRoute.create(path, route, transformer));
    }

    /**
     * Map the route for HTTP HEAD requests
     *
     * @param path        the path
     * @param acceptType  the accept type
     * @param route       The route
     * @param transformer the response transformer
     */
    public static void head(String path, String acceptType, Route route, ResponseTransformer transformer) {
        route(HttpMethod.HEAD, SimpleResponseTransformerRoute.create(path, acceptType, route, transformer));
    }

    /**
     * Map the route for HTTP CONNECT requests
     *
     * @param path        the path
     * @param route       The route
     * @param transformer the response transformer
     */
    public static void connect(final String path, final Route route, ResponseTransformer transformer) {
        route(HttpMethod.CONNECT, SimpleResponseTransformerRoute.create(path, route, transformer));
    }

    /**
     * Map the route for HTTP CONNECT requests
     *
     * @param path        the path
     * @param acceptType  the accept type
     * @param route       The route
     * @param transformer the response transformer
     */
    public static void connect(String path,
                               String acceptType,
                               Route route,
                               ResponseTransformer transformer) {
        route(HttpMethod.CONNECT, SimpleResponseTransformerRoute.create(path, acceptType, route, transformer));
    }

    /**
     * Map the route for HTTP TRACE requests
     *
     * @param path        the path
     * @param route       The route
     * @param transformer the response transformer
     */
    public static void trace(final String path, final Route route, ResponseTransformer transformer) {
        route(HttpMethod.TRACE, SimpleResponseTransformerRoute.create(path, route, transformer));
    }

    /**
     * Map the route for HTTP TRACE requests
     *
     * @param path        the path
     * @param acceptType  the accept type
     * @param route       The route
     * @param transformer the response transformer
     */
    public static void trace(String path,
                             String acceptType,
                             Route route,
                             ResponseTransformer transformer) {
        route(HttpMethod.TRACE, SimpleResponseTransformerRoute.create(path, acceptType, route, transformer));
    }

    /**
     * Map the route for HTTP OPTIONS requests
     *
     * @param path        the path
     * @param route       The route
     * @param transformer the response transformer
     */
    public static void options(final String path, final Route route, ResponseTransformer transformer) {
        route(HttpMethod.OPTIONS, SimpleResponseTransformerRoute.create(path, route, transformer));
    }

    /**
     * Map the route for HTTP OPTIONS requests
     *
     * @param path        the path
     * @param acceptType  the accept type
     * @param route       The route
     * @param transformer the response transformer
     */
    public static void options(String path,
                               String acceptType,
                               Route route,
                               ResponseTransformer transformer) {
        route(HttpMethod.OPTIONS, SimpleResponseTransformerRoute.create(path, acceptType, route, transformer));
    }

    /**
     * Map the route for HTTP PATCH requests
     *
     * @param path        the path
     * @param route       The route
     * @param transformer the response transformer
     */
    public static void patch(final String path, final Route route, ResponseTransformer transformer) {
        route(HttpMethod.PATCH, SimpleResponseTransformerRoute.create(path, route, transformer));
    }

    /**
     * Map the route for HTTP PATCH requests
     *
     * @param path        the path
     * @param acceptType  the accept type
     * @param route       The route
     * @param transformer the response transformer
     */
    public static void patch(String path,
                             String acceptType,
                             Route route,
                             ResponseTransformer transformer) {
        route(HttpMethod.PATCH, SimpleResponseTransformerRoute.create(path, acceptType, route, transformer));
    }

    //////////////////////////////////////////////////
    // END Response Transforming Routes
    //////////////////////////////////////////////////

    /* ***************************** 异常处理 **************************** */

    public static void exception(Class<? extends Exception> exceptionClass, ExceptionHandler handler) {
        ExceptionMapper.singleton().map(exceptionClass, new SimpleExceptionHandler(exceptionClass) {
            @Override
            public void apply(Exception exception, Request request, Response response) {
                handler.apply(exception, request, response);
            }
        });
    }

    /* ***************************** 停止 **************************** */

    public static void halt() {
        throw new HaltException();
    }

    public static void halt(int status) {
        throw new HaltException(status);
    }

    public static void halt(String body) {
        throw new HaltException(body);
    }

    public static void halt(int status, String body) {
        throw new HaltException(status, body);
    }

    public static void halt(HttpResponseStatus status) {
        throw new HaltException(status);
    }


    //////////////////////////////////////////////////
    // model and view helper method
    //////////////////////////////////////////////////


    /**
     * Constructs a ModelAndView with the provided model and view name
     *
     * @param model    the model
     * @param viewName the view name
     * @return the model and view
     */
    public static ModelAndView modelAndView(Map<String, Object> model, String viewName) {
        return new ModelAndView(model, viewName);
    }


//    public static  void singleton(final String path, final Supplier<?> route) {
//        singleton(path, (request, response) -> route.singleton());
//    }
//    public static  void get2(final String path, final BiFunction<Request, Response, ?> route) {
//
//    }
}
